use std::boxed::Box;

use opencv::{
    prelude::*,
    videostab::{
        prelude::*,
        OnePassStabilizer,
        TwoPassStabilizer,
        GaussianMotionFilter,
        KeypointBasedMotionEstimator,
        RansacParams,
        MotionModel,
        MotionEstimatorRansacL2,
        VideoFileSource,
    },
    features2d::{self, Feature2D},
    videoio::VideoWriter,
    types,highgui,
    core::{self, Ptr, BorderTypes, Size},
};
#[macro_use]
extern crate opencv;

fn main() {

    // let inputPath = "./data/gopro7day.mp4";
    // let inputPath = "./data/car.avi";
    let inputPath = "./data/gopro6.mp4";
    let outputPath = "./data/output.mp4";
    cac_stab_video(inputPath, outputPath);

}

fn cac_stab_video(inputPath: &str, outputPath: &str)
{
    // 运动估计
    let est_para = 0.1f32;
    let mut est = MotionEstimatorRansacL2::new(MotionModel::MM_AFFINE).unwrap();

    // Ransac参数设置
    let ransac = RansacParams::new(
        3, 5.0, 0.5, 0.8
    ).unwrap();

    // Ransac计算
    est.set_ransac_params(&ransac);
    est.set_min_inlier_ratio(est_para);

    // 运动估计关键点匹配
    let mut motion_est_builder = KeypointBasedMotionEstimator::new(Ptr::new(est).into()).unwrap();

    // 
    let ptr_feature_detector = FastFeatureDetector::create(10, true, 2).unwrap();

    // 
    motion_est_builder.set_detector(ptr_feature_detector.into()).unwrap();

    // 3-Prepare the stabilizer
    let mut stabilizer;
    // first, prepare the one or two pass stabilizer
    let radius_pass = 15;
    // with a two pass stabilizer
    let est_trim = true;
    let mut two_pass_stabilizer = TwoPassStabilizer::default().unwrap();
    two_pass_stabilizer.set_estimate_trim_ratio(est_trim);
    two_pass_stabilizer.set_motion_stabilizer(
        Ptr::new(GaussianMotionFilter::new(radius_pass, -1.0).unwrap()).into()
    );
    stabilizer = two_pass_stabilizer;

    let mut src_video = VideoFileSource::new(inputPath, false).unwrap();
    let src_video_frame_count = src_video.count().unwrap() as usize;
    let src_video_frame_width = src_video.width().unwrap();
    let src_video_frame_height = src_video.height().unwrap();
    let src_video_frame_fps = src_video.fps().unwrap();
    println!("src_video count: {}, width: {}, height: {}, fps: {}",
        src_video.count().unwrap(), src_video.width().unwrap(), src_video.height().unwrap(), src_video.fps().unwrap(),
    );

    let mut ptr_src_video = Ptr::new(src_video);

    // second, set up the parameters
    let radius = 15;
    let trim_ratio = 0.1;
    let incl_constr = false;
    stabilizer.set_frame_source(ptr_src_video.into());
    stabilizer.set_motion_estimator(Ptr::new(motion_est_builder).into());
    stabilizer.set_radius(radius);
    stabilizer.set_trim_ratio(trim_ratio);
    stabilizer.set_correction_for_inclusion(incl_constr);
    stabilizer.set_border_mode(core::BORDER_REPLICATE);
    println!("stabilizer: {}", 1);

    let mut nframes = 0usize;
    let opened = false;

    // let mut writer: VideoWriter = VideoWriter::default().unwrap();
    // let opened = writer.open(
    //     outputPath,
    //     VideoWriter::fourcc(
    //         b'X' as i8, b'V' as i8,
    //         b'I' as i8, b'D' as i8).unwrap(),
    //     src_video_frame_fps as f64,
    //     Size::new(src_video_frame_width, src_video_frame_height),
    //     true,
    // ).unwrap();
    println!("output file({}) open state is: {}", outputPath, opened);
    while let Ok(stab_frame) = TwoPassStabilizerTrait::next_frame(&mut stabilizer) {
        println!("stab_frame size: {:?}", stab_frame.size());
        println!("src_video_frame_count: {}, nFrames: {}", src_video_frame_count, nframes);
        if nframes >= src_video_frame_count {
            break;
        }
        println!(" nFrames: {:?}", stab_frame.flags());
        // writer.write(&stab_frame);
        highgui::imshow("stabilizedFrame", &stab_frame);
        highgui::wait_key(10);
        nframes += 1;
    }
    // writer.release();
    println!("done");
}
